using System;
using System.Collections;
using System.Collections.Generic;
using Server;
using Server.Spells;
using Server.Spells.Necromancy;
using Server.Spells.First;
using Server.Spells.Second;
using Server.Spells.Fourth;
using Server.Spells.Fifth;
using Server.Targeting;
using Server.Mobiles;
using Server.Items;
using Server.Network;
using Server.ContextMenus;

namespace Server.Items
{
    public enum MagicalFood
    {
        None = 0x0,
        WrathGrapes = 0x1,
        EnchantedApple = 0x2,
    }

    public class BaseMagicalFood : Food
    {
        public virtual MagicalFood FoodID { get { return MagicalFood.None; } }
        public virtual TimeSpan Cooldown { get { return TimeSpan.Zero; } }
        public virtual TimeSpan Duration { get { return TimeSpan.Zero; } }
        public virtual int EatMessage { get { return 0; } }

        [Constructable]
        public BaseMagicalFood(int itemID)
            : base(itemID)
        {
            Weight = 1.0;
            FillFactor = 0;
            Stackable = false;
        }

        public BaseMagicalFood(Serial serial)
            : base(serial)
        {
        }

        public override bool Eat(Mobile from)
        {
            if (!IsUnderInfluence(from, FoodID))
            {
                if (!CoolingDown(from, FoodID))
                {
                    from.SendLocalizedMessage(EatMessage);

                    StartInfluence(from, FoodID, Duration, Cooldown);
                    Consume();

                    return true;
                }
                else
                    from.SendLocalizedMessage(1070772); // You must wait a few seconds before you can use that item.
            }

            return false;
        }

        private static Hashtable m_Table;
        private static Hashtable m_Cooldown;

        public static bool IsUnderInfluence(Mobile mob, MagicalFood id)
        {
            if (m_Table != null && m_Table[mob] != null && ((int)m_Table[mob] & (int)id) > 0)
                return true;

            return false;
        }

        public static bool CoolingDown(Mobile mob, MagicalFood id)
        {
            if (m_Cooldown != null && m_Cooldown[mob] != null && ((int)m_Cooldown[mob] & (int)id) > 0)
                return true;

            return false;
        }

        public static void StartInfluence(Mobile mob, MagicalFood id, TimeSpan duration, TimeSpan cooldown)
        {
            if (m_Table == null)
                m_Table = new Hashtable();

            if (m_Table[mob] == null)
                m_Table[mob] = 0;

            m_Table[mob] = (int)m_Table[mob] | (int)id;

            Timer.DelayCall(duration, new TimerStateCallback(EndInfluence), new object[] { mob, id, cooldown });
        }

        public static void EndInfluence(object obj)
        {
            if (obj is object[] && (((object[])obj).Length == 3))
            {
                object[] args = (object[])obj;

                if (args[0] is Mobile && args[1] is MagicalFood && args[2] is TimeSpan)
                    EndInfluence((Mobile)args[0], (MagicalFood)args[1], (TimeSpan)args[2]);
            }
        }

        public static void EndInfluence(Mobile mob, MagicalFood id, TimeSpan cooldown)
        {
            m_Table[mob] = (int)m_Table[mob] & ~((int)id);

            if (cooldown != TimeSpan.Zero)
            {
                if (m_Cooldown == null)
                    m_Cooldown = new Hashtable();

                if (m_Cooldown[mob] == null)
                    m_Cooldown[mob] = 0;

                m_Cooldown[mob] = (int)m_Cooldown[mob] | (int)id;

                Timer.DelayCall(cooldown, new TimerStateCallback(EndCooldown), new object[] { mob, id });
            }
        }

        public static void EndCooldown(object obj)
        {
            if (obj is object[] && (((object[])obj).Length == 2))
            {
                object[] args = (object[])obj;

                if (args[0] is Mobile && args[1] is MagicalFood)
                    EndCooldown((Mobile)args[0], (MagicalFood)args[1]);
            }
        }

        public static void EndCooldown(Mobile mob, MagicalFood id)
        {
            m_Cooldown[mob] = (int)m_Cooldown[mob] & ~((int)id);
        }

        public override void Serialize(GenericWriter writer)
        {
            base.Serialize(writer);

            writer.Write((int)0); // version
        }

        public override void Deserialize(GenericReader reader)
        {
            base.Deserialize(reader);

            int version = reader.ReadInt();
        }
    }

    public abstract class Food : Item, IEngravable
    {
        private Mobile m_Poisoner;
        private Poison m_Poison;
        private int m_FillFactor;

        [CommandProperty(AccessLevel.GameMaster)]
        public Mobile Poisoner
        {
            get { return m_Poisoner; }
            set { m_Poisoner = value; }
        }

        [CommandProperty(AccessLevel.GameMaster)]
        public Poison Poison
        {
            get { return m_Poison; }
            set { m_Poison = value; }
        }

        [CommandProperty(AccessLevel.GameMaster)]
        public int FillFactor
        {
            get { return m_FillFactor; }
            set { m_FillFactor = value; }
        }

        private string m_EngravedText;

        [CommandProperty(AccessLevel.GameMaster)]
        public string EngravedText
        {
            get { return m_EngravedText; }
            set { m_EngravedText = value; InvalidateProperties(); }
        }

        public Food(int itemID)
            : this(1, itemID)
        {
        }

        public Food(int amount, int itemID)
            : base(itemID)
        {
            Stackable = true;
            Amount = amount;
            m_FillFactor = 1;
        }

        public Food(Serial serial)
            : base(serial)
        {
        }

        public override void GetContextMenuEntries(Mobile from, List<ContextMenuEntry> list)
        {
            base.GetContextMenuEntries(from, list);

            if (from.Alive)
                list.Add(new ContextMenus.EatEntry(from, this));
        }

        public override void OnDoubleClick(Mobile from)
        {
            if (!Movable)
                return;

            if (from.InRange(this.GetWorldLocation(), 1))
            {
                Eat(from);
            }
        }

        public override void GetProperties(ObjectPropertyList list)
        {
            base.GetProperties(list);

            if (m_EngravedText != null)
                list.Add(1073183, m_EngravedText); // Decorated: ~1_MESSAGE~
        }

        public virtual bool Eat(Mobile from)
        {
            // Fill the Mobile with FillFactor
            if (FillHunger(from, m_FillFactor))
            {
                // Play a random "eat" sound
                from.PlaySound(Utility.Random(0x3A, 3));

                if (from.Body.IsHuman && !from.Mounted)
                    from.Animate(34, 5, 1, true, false, 0);

                if (m_Poison != null)
                    from.ApplyPoison(m_Poisoner, m_Poison);

                Consume();

                return true;
            }

            return false;
        }

        static public bool FillHunger(Mobile from, int fillFactor)
        {
            if (from.Hunger >= 20)
            {
                from.SendLocalizedMessage(500867); // You are simply too full to eat any more!
                return false;
            }

            int iHunger = from.Hunger + fillFactor;
            if (from.Stam < from.StamMax)
                from.Stam += Utility.Random(6, 3) + fillFactor / 5;//restore some stamina
            if (iHunger >= 20)
            {
                from.Hunger = 20;
                from.SendLocalizedMessage(500872); // You manage to eat the food, but you are stuffed!
            }
            else
            {
                from.Hunger = iHunger;

                if (iHunger < 5)
                    from.SendLocalizedMessage(500868); // You eat the food, but are still extremely hungry.
                else if (iHunger < 10)
                    from.SendLocalizedMessage(500869); // You eat the food, and begin to feel more satiated.
                else if (iHunger < 15)
                    from.SendLocalizedMessage(500870); // After eating the food, you feel much less hungry.
                else
                    from.SendLocalizedMessage(500871); // You feel quite full after consuming the food.
            }

            return true;
        }

        public override void Serialize(GenericWriter writer)
        {
            base.Serialize(writer);

            writer.Write((int)5); // version

            writer.Write((string)m_EngravedText);

            writer.Write(m_Poisoner);

            Poison.Serialize(m_Poison, writer);
            writer.Write(m_FillFactor);
        }

        public override void Deserialize(GenericReader reader)
        {
            base.Deserialize(reader);

            int version = reader.ReadInt();

            switch (version)
            {
                case 5:
                    {
                        m_EngravedText = reader.ReadString();

                        goto case 4;
                    }
                case 1:
                    {
                        switch (reader.ReadInt())
                        {
                            case 0: m_Poison = null; break;
                            case 1: m_Poison = Poison.Lesser; break;
                            case 2: m_Poison = Poison.Regular; break;
                            case 3: m_Poison = Poison.Greater; break;
                            case 4: m_Poison = Poison.Deadly; break;
                        }

                        break;
                    }
                case 2:
                    {
                        m_Poison = Poison.Deserialize(reader);
                        break;
                    }
                case 3:
                    {
                        m_Poison = Poison.Deserialize(reader);
                        m_FillFactor = reader.ReadInt();
                        break;
                    }
                case 4:
                    {
                        m_Poisoner = reader.ReadMobile();
                        goto case 3;
                    }
            }
        }
    }

    //----------------------------------------------------------------------------//

    /// <summary>
    /// Below Contains The Servers Default Cooking Engine
    /// </summary>

    public class UtilityItem
    {
        static public int RandomChoice(int itemID1, int itemID2)
        {
            int iRet = 0;
            switch (Utility.Random(2))
            {
                default:
                case 0: iRet = itemID1; break;
                case 1: iRet = itemID2; break;
            }
            return iRet;
        }
    }

    public abstract class CookableFood : Item
    {
        private int m_CookingLevel;

        [CommandProperty(AccessLevel.GameMaster)]
        public int CookingLevel
        {
            get
            {
                return m_CookingLevel;
            }
            set
            {
                m_CookingLevel = value;
            }
        }

        public CookableFood(int itemID, int cookingLevel)
            : base(itemID)
        {
            m_CookingLevel = cookingLevel;
        }

        public CookableFood(Serial serial)
            : base(serial)
        {
        }

        public abstract Food Cook();

        public override void Serialize(GenericWriter writer)
        {
            base.Serialize(writer);

            writer.Write((int)1); // version
            // Version 1
            writer.Write((int)m_CookingLevel);

        }

        public override void Deserialize(GenericReader reader)
        {
            base.Deserialize(reader);

            int version = reader.ReadInt();

            switch (version)
            {
                case 1:
                    {
                        m_CookingLevel = reader.ReadInt();

                        break;
                    }
            }
        }

#if false
		public override void OnDoubleClick( Mobile from )
		{
			if ( !Movable )
				return;

			from.Target = new InternalTarget( this );
		}
#endif
        public static bool IsHeatSource(object targeted)
        {
            int itemID;

            if (targeted is Item)
                itemID = ((Item)targeted).ItemID;
            else if (targeted is StaticTarget)
                itemID = ((StaticTarget)targeted).ItemID;
            else
                return false;

            if (itemID >= 0xDE3 && itemID <= 0xDE9)
                return true; // Campfire
            else if (itemID >= 0x461 && itemID <= 0x48E)
                return true; // Sandstone oven/fireplace
            else if (itemID >= 0x92B && itemID <= 0x96C)
                return true; // Stone oven/fireplace
            else if (itemID == 0xFAC)
                return true; // Firepit
            else if (itemID >= 0x184A && itemID <= 0x184C)
                return true; // Heating stand (left)
            else if (itemID >= 0x184E && itemID <= 0x1850)
                return true; // Heating stand (right)
            else if (itemID >= 0x398C && itemID <= 0x399F)
                return true; // Fire field

            return false;
        }

        private class InternalTarget : Target
        {
            private CookableFood m_Item;

            public InternalTarget(CookableFood item)
                : base(1, false, TargetFlags.None)
            {
                m_Item = item;
            }

            protected override void OnTarget(Mobile from, object targeted)
            {
                if (m_Item.Deleted) return;

                if (CookableFood.IsHeatSource(targeted))
                {
                    if (from.BeginAction(typeof(CookableFood)))
                    {
                        from.PlaySound(0x225);

                        m_Item.Consume();

                        InternalTimer t = new InternalTimer(from, targeted as IPoint3D, from.Map, m_Item);
                        t.Start();
                    }
                    else
                    {
                        from.SendLocalizedMessage(500119); // You must wait to perform another action
                    }
                }
            }

            private class InternalTimer : Timer
            {
                private Mobile m_From;
                private IPoint3D m_Point;
                private Map m_Map;
                private CookableFood m_CookableFood;

                public InternalTimer(Mobile from, IPoint3D p, Map map, CookableFood cookableFood)
                    : base(TimeSpan.FromSeconds(5.0))
                {
                    m_From = from;
                    m_Point = p;
                    m_Map = map;
                    m_CookableFood = cookableFood;
                }

                protected override void OnTick()
                {
                    m_From.EndAction(typeof(CookableFood));

                    if (m_From.Map != m_Map || (m_Point != null && m_From.GetDistanceToSqrt(m_Point) > 3))
                    {
                        m_From.SendLocalizedMessage(500686); // You burn the food to a crisp! It's ruined.
                        return;
                    }

                    if (m_From.CheckSkill(SkillName.Cooking, m_CookableFood.CookingLevel, 100))
                    {
                        Food cookedFood = m_CookableFood.Cook();

                        if (m_From.AddToBackpack(cookedFood))
                            m_From.PlaySound(0x57);
                    }
                    else
                    {
                        m_From.SendLocalizedMessage(500686); // You burn the food to a crisp! It's ruined.
                    }
                }
            }
        }
    }

    //----------------------------------------------------------------------------//

    /// <summary>
    /// BaseMagicFish - Wonderous, HighlyPeculiar, Truly Rare, And Prized Fish
    /// </summary>

    public abstract class BaseMagicFish : Item
	{
		public virtual int Bonus{ get{ return 0; } }
		public virtual StatType Type{ get{ return StatType.Str; } }

		public override double DefaultWeight
		{
			get { return 1.0; }
		}

		public BaseMagicFish( int hue ) : base( 0xDD6 )
		{
			Hue = hue;
		}

		public BaseMagicFish( Serial serial ) : base( serial )
		{
		}

		public virtual bool Apply( Mobile from )
		{
			bool applied = Spells.SpellHelper.AddStatOffset( from, Type, Bonus, TimeSpan.FromMinutes( 1.0 ) );

			if ( !applied )
				from.SendLocalizedMessage( 502173 ); // You are already under a similar effect.

			return applied;
		}

		public override void OnDoubleClick( Mobile from )
		{
			if ( !IsChildOf( from.Backpack ) )
			{
				from.SendLocalizedMessage( 1042001 ); // That must be in your pack for you to use it.
			}
			else if ( Apply( from ) )
			{
				from.FixedEffect( 0x375A, 10, 15 );
				from.PlaySound( 0x1E7 );
				from.LocalOverheadMessage( MessageType.Regular, 0x3B2, 501774 ); // You swallow the fish whole!
				Delete();
			}
		}

		public override void Serialize( GenericWriter writer )
		{
			base.Serialize( writer );

			writer.Write( (int) 0 ); // version
		}

		public override void Deserialize( GenericReader reader )
		{
			base.Deserialize( reader );

			int version = reader.ReadInt();
		}
	}
}